#ifndef _OCPP_DefaultWorkQueueBase_H__
#define _OCPP_DefaultWorkQueueBase_H__

#include "WorkQueue.h"
#include "RequestHandlerHolder.h"

namespace OCPP
{
	class _MainExport DefaultWorkQueueBase : public WorkQueue
	{
	public:
		/** Constructor.
				Call startup() to initialise.
			@param name Optional name, just helps to identify logging output
			*/
		DefaultWorkQueueBase(const String& name = "");
		virtual ~DefaultWorkQueueBase();
		/// Get the name of the work queue
		const String& getName() const;
		/** Get the number of worker threads that this queue will start when
			startup() is called.
		*/
		virtual size_t getWorkerThreadCount() const;

		/** Set the number of worker threads that this queue will start
			when startup() is called (default 1).
			Calling this will have no effect unless the queue is shut down and
			restarted.
		*/
		virtual void setWorkerThreadCount(size_t c);

		/** Process the next request on the queue.
		@remarks
			This method is public, but only intended for advanced users to call.
			The only reason you would call this, is if you were using your
			own thread to drive the worker processing. The thread calling this
			method will be the thread used to call the RequestHandler.
		*/
		virtual void _processNextRequest();

		/// Main function for each thread spawned.
		virtual void _threadMain() = 0;

		/** Returns whether the queue is trying to shut down. */
		virtual bool isShuttingDown() const { return mShuttingDown; }

		/// @copydoc WorkQueue::addRequestHandler
		virtual void addRequestHandler(uint16 channel, RequestHandler* rh);
		/// @copydoc WorkQueue::removeRequestHandler
		virtual void removeRequestHandler(uint16 channel, RequestHandler* rh);
		/// @copydoc WorkQueue::addResponseHandler
		virtual void addResponseHandler(uint16 channel, ResponseHandler* rh);
		/// @copydoc WorkQueue::removeResponseHandler
		virtual void removeResponseHandler(uint16 channel, ResponseHandler* rh);
	
		/// @copydoc WorkQueue::addRequest
		virtual RequestID addRequest(uint16 channel, uint16 requestType, const Any& rData, int priority = 0, uint8 retryCount = 0,
			bool forceSynchronous = false);

		/// @copydoc WorkQueue::abortRequest
		virtual void abortRequest(RequestID id);
		/// @copydoc WorkQueue::abortRequestsByChannel
		virtual void abortRequestsByChannel(uint16 channel);
		/// @copydoc WorkQueue::abortAllRequests
		virtual void abortAllRequests();
		/// @copydoc WorkQueue::setPaused
		virtual void setPaused(bool pause);
		/// @copydoc WorkQueue::isPaused
		virtual bool isPaused() const;
		/// @copydoc WorkQueue::setRequestsAccepted
		virtual void setRequestsAccepted(bool accept);
		/// @copydoc WorkQueue::getRequestsAccepted
		virtual bool getRequestsAccepted() const;
		/// @copydoc WorkQueue::processResponses
		virtual void processResponses();
		/// @copydoc WorkQueue::getResponseProcessingTimeLimit
		virtual unsigned long getResponseProcessingTimeLimit() const { return mResposeTimeLimitMS; }
		/// @copydoc WorkQueue::setResponseProcessingTimeLimit
		virtual void setResponseProcessingTimeLimit(unsigned long ms) { mResposeTimeLimitMS = ms; }
	protected:
		String mName;
		size_t mWorkerThreadCount;
		bool mIsRunning;
		unsigned long mResposeTimeLimitMS;

		typedef std::priority_queue<std::pair<int,Request*> > PriRequestQueue;
		typedef std::deque<Request*> RequestQueue;
		typedef std::deque<Response*> ResponseQueue;
		PriRequestQueue mRequestQueue;
		RequestQueue mProcessQueue;
		ResponseQueue mResponseQueue;

		typedef std::shared_ptr<RequestHandlerHolder> RequestHandlerHolderPtr;
		typedef std::list<RequestHandlerHolderPtr> RequestHandlerList;
		typedef std::list<ResponseHandler*> ResponseHandlerList;
		typedef std::map<uint16, RequestHandlerList> RequestHandlerListByChannel;
		typedef std::map<uint16, ResponseHandlerList> ResponseHandlerListByChannel;

		RequestHandlerListByChannel mRequestHandlers;
		ResponseHandlerListByChannel mResponseHandlers;
		RequestID mRequestCount;
		bool mPaused;
		bool mAcceptRequests;
		bool mShuttingDown;

		CORE_MUTEX(mRequestMutex);
		CORE_MUTEX(mProcessMutex);
		CORE_MUTEX(mResponseMutex);
		CORE_RW_MUTEX(mRequestHandlerMutex);

		void processRequestResponse(Request* r, bool synchronous);
		Response* processRequest(Request* r);
		void processResponse(Response* r);
		/// Notify workers about a new request. 
		virtual void notifyWorkers() = 0;
		/// Put a Request on the queue with a specific RequestID.
		void addRequestWithRID(RequestID rid, uint16 channel, uint16 requestType, const Any& rData, uint8 retryCount);
	};
}
#endif
